, кавычки, пробелы — всё чистим. // Нет файла / пустой / мусор — аварийный 503 (сломана конфигурация). function load_keitaro_token(): string { $file = __DIR__ . '/' . TOKEN_FILE; if (!is_readable($file)) { fail(503); } $raw = file_get_contents($file); if ($raw === false) { fail(503); } $token = preg_replace('/[^A-Za-z0-9_.\-]/', '', $raw) ?? ''; $token = trim($token); // Токен кампании — буквы/цифры/_-/. Без пробелов и прочего внутри if ($token === '' || !preg_match('/^[A-Za-z0-9_\.\-]{8,128}$/', $token)) { fail(503); } return $token; } // ────────────────── Отдача локальных файлов ──────────────── // Отдать локальный html-файл как есть (wht.html и пр.) function serve_file(string $name): void { while (ob_get_level() > 0) { ob_end_clean(); } $file = __DIR__ . '/' . $name; if (is_file($file)) { if (!headers_sent()) { http_response_code(200); header('Content-Type: text/html; charset=UTF-8'); } readfile($file); exit; } fail(503); } // Отдать файл с заменой плейсхолдера на URL оффера function serve_offer(string $name, string $offerUrl): void { while (ob_get_level() > 0) { ob_end_clean(); } $file = __DIR__ . '/' . $name; if (!is_file($file)) { fail(503); } $html = file_get_contents($file); if ($html !== false && $offerUrl !== '') { $safeUrl = htmlspecialchars($offerUrl, ENT_QUOTES, 'UTF-8'); $html = str_replace(OFFER_PH, $safeUrl, $html); } if (!headers_sent()) { http_response_code(200); header('Content-Type: text/html; charset=UTF-8'); } echo $html; exit; } function serve_white(): void { serve_file('wht.html'); } register_shutdown_function(static function (): void { $e = error_get_last(); if ($e !== null && in_array($e['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) { while (ob_get_level() > 0) { ob_end_clean(); } if (!headers_sent()) { http_response_code(503); header('Retry-After: 30'); } } }); set_exception_handler(static function (\Throwable $e): void { fail(503); }); // ──────────────────────── Роутинг ───────────────────────── // Повторный посетитель: кука хранит закодированный URL оффера, // поэтому в Keitaro не ходим вообще. Старое значение 'true' // (если было до обновления) прогоняем через полный цикл. $maybeCookie = null; if (isset($_COOKIE[COOKIE_NAME]) && $_COOKIE[COOKIE_NAME] !== '') { $v = $_COOKIE[COOKIE_NAME]; if ($v !== 'true') { serve_offer(OFFER_FILE, rawurldecode($v)); } else { $maybeCookie = true; } } if ($maybeCookie === true) { // Устаревшая кука 'true': снесём, дальше пойдём обычным путём setcookie(COOKIE_NAME, '', time() - 3600, '/'); } // Стандартный клиент Keitaro 9.4.9.5. // (Если ваш файл клиента называется kclll.php — поменяйте имя здесь.) require_once __DIR__ . '/kclll.php'; // Токен кампании — из keitaro_token.txt (не хардкод). $keitaroToken = load_keitaro_token(); try { $client = new KClickClient(KEITARO_URL, $keitaroToken); $client->sendAllParams(); $result = $client->performRequest(); if (!is_object($result)) { serve_white(); } if (!empty($result->error)) { serve_white(); } $headers = (array)(isset($result->headers) ? $result->headers : []); foreach ($headers as $header) { $header = is_string($header) ? stripslashes($header) : ''; if (strncmp($header, SENTINEL_PREFIX, strlen(SENTINEL_PREFIX)) !== 0) { continue; } $localFile = explode('?', substr($header, strlen(SENTINEL_PREFIX)), 2)[0]; if ($localFile === OFFER_FILE) { // Ссылка на оффер, выбранный Keitaro (ротация/приоритеты) $offerUrl = $client->getOffer(); if ($offerUrl === 'no_offer') { // Нет активного оффера (или токена лендинга) serve_file($localFile); } // Кэшируем выбор оффера в куку if (!headers_sent()) { setcookie( COOKIE_NAME, rawurlencode($offerUrl), time() + 86400 * COOKIE_TTL_DAYS, '/' ); } serve_offer($localFile, $offerUrl); } if ($localFile !== '' && is_file(__DIR__ . '/' . $localFile)) { serve_file($localFile); } } $client->executeAndBreak(); serve_white(); } catch (\Throwable $e) { serve_white(); }